package dataImport.action;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import dataImport.action.abstracts.AbstractEntityReader;
import dataImport.action.helper.UnityGuardian;
import dataImport.action.manager.EntityReaderManager;
import dataImport.exception.DuplicateAttributeException;
import dataImport.exception.EntityNotFoundException;
import dataImport.exception.MissingAttributeException;
import dataImport.exception.NoEntityRelatedWithException;
import dataImport.exception.UniquenessRestrictionViolatedException;
import dataImport.model.CustomAction;
import dataImport.model.Object;
import dataImport.model.Place;
import dataImport.model.PredefinedAction;
import dataImport.model.State;
import dataImport.model.StaticAction;
import dataImport.model.StaticAction.StaticActionRequirement;
import dataImport.model.Text;
import dataImport.model.abstracts.AbstractEntity;


public class PredefinedActionReader extends AbstractEntityReader {

	private static final long serialVersionUID = 6982676265057367342L;

	private static final String ATTRIBUTE_VALUE = "value";

	private UnityGuardian commandActionIdStaticActionEntityId;

	public PredefinedActionReader(final EntityReaderManager entityReaderManager) {
		super(entityReaderManager);
		this.commandActionIdStaticActionEntityId = new UnityGuardian();
	}

	@Override
	protected AbstractEntity createEntity(Element element) {
		final String name = element.getNodeName();
		final String value = element.getAttribute(ATTRIBUTE_VALUE);
		final Element parentNode = (Element) element.getParentNode();
		final String parentNodeName = parentNode.getNodeName();
		final String parentNodeId = parentNode.getAttribute(AbstractEntityReader.ATTRIBUTE_ID);
		final int position = this.findNodePosition(element, parentNode.getChildNodes());

		this.verifyRestrictions(parentNodeName, parentNodeId, name, value);

		final CustomAction customAction = (CustomAction) super.getEntityReaderManager().getCustomActionReader().getEntityWithId(parentNodeId);
		final StaticAction staticAction = StaticAction.get(name);

		switch (staticAction) {
			case CREATEOBJECT:
			case DESTROYOBJECT:
			case SHOWOBJECTDESCRIPTION:
				final Object object = (Object) super.getEntityReaderManager().getObjectReader().getEntityWithId(value);
				return new PredefinedAction(customAction, position, staticAction, object);
			case CREATESTATE:
			case DESTROYSTATE:
				final State state = (State) super.getEntityReaderManager().getStateReader().getEntityWithId(value);
				return new PredefinedAction(customAction, position, staticAction, state);
			case ACTIVATEACTION:
			case DEACTIVATEACTION:
				final CustomAction action = (CustomAction) super.getEntityReaderManager().getCustomActionReader().getEntityWithId(value);
				return new PredefinedAction(customAction, position, staticAction, action);
			case ADDPLACE:
			case REMOVEPLACE:
			case SHOWPLACEDESCRIPTION:
				final Place place = (Place) super.getEntityReaderManager().getPlaceReader().getEntityWithId(value);
				return new PredefinedAction(customAction, position, staticAction, place);
			case EMOTE:
				final Text text = new Text(value);
				return new PredefinedAction(customAction, position, staticAction, text);
			case LISTACTIONS:
				return new PredefinedAction(customAction, position, staticAction);
			default:
				break;
		}

		return null;
	}

	protected int findNodePosition(final Element element, final NodeList siblings) {
		int position = 0;

		for (int i = 0; i < siblings.getLength(); i++) {
			if (element.equals(siblings.item(i))) {
				return position;
			}

			if (siblings.item(i).getNodeType() == Node.ELEMENT_NODE) {
				position += 1;
			}
		}

		throw new EntityNotFoundException(element.getAttribute(ATTRIBUTE_VALUE));
	}

	@Override
	public void read(Document document) {
		for (StaticAction staticAction : StaticAction.values()) {
			super.read(staticAction.getName(), document);
		}
	}

	private void verifyRestrictions(final String commandActionName, final String commandActionId, final String staticActionName, final String entityId) {
		if (StaticAction.get(staticActionName).getRequirement() == StaticActionRequirement.ENTITY) {
			if (entityId.isEmpty()) {
				throw new MissingAttributeException(ATTRIBUTE_VALUE);
			}
		}

		try {
			this.commandActionIdStaticActionEntityId.ensureUniqueness(commandActionId, staticActionName, entityId);
		}
		catch (final UniquenessRestrictionViolatedException e) {
			throw new DuplicateAttributeException("predefinedAction", staticActionName);
		}

		if (!commandActionName.equals(CustomActionReader.ENTITY_NAME)) {
			throw new NoEntityRelatedWithException(staticActionName, CustomActionReader.ENTITY_NAME);
		}
	}
}
